﻿/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_STREAM_TEMPLATE_H
#define _GCTL_STREAM_TEMPLATE_H

#include "vector"
#include "string"
#include "sstream"

#include "../core/exceptions.h"


namespace gctl
{
	template <typename ObjValType>
	void type2ss(ObjValType in_val, std::stringstream &out_ss)
	{
		out_ss.clear();
		out_ss << in_val;
		return;
	}

	template <typename OutValType>
	int str2type(std::string in_str, OutValType &out_val)
	{
		std::stringstream tmp_ss;
		type2ss(in_str, tmp_ss);

		tmp_ss >> out_val;

		if (tmp_ss.fail())
		{
			return -1;
		}
		return 0;
	}

	template <typename OutValType>
	void str2type_vector(const std::vector<std::string> &in_vec, std::vector<OutValType> &out_data)
	{
		if (!out_data.empty()){out_data.clear();}

		out_data.resize(in_vec.size());
		for (int i = 0; i < out_data.size(); i++)
		{
			str2type(in_vec[i], out_data[i]);
		}
		return;
	}

	/**
	 * @brief      Parse a string argument into an element array
	 *
	 * @param[in]  val_str    The input value string
	 * @param      out_vec    The output array
	 * @param[in]  separator  The separator
	 *
	 * @tparam     ValueType  Runtime value type
	 *
	 * @return     0 for success, -1 for failure
	 */
	template <typename ValueType>
	int parse_string_to_vector(std::string val_str, char separator, std::vector<ValueType> &out_vec)
	{
		val_str.erase(0, val_str.find_first_not_of(" \t"));
		val_str.erase(val_str.find_last_not_of(" \t") + 1);

		int pos;
		bool bk = false;
		ValueType val;
		std::string l_str;
		while (1)
		{
			pos = val_str.find(separator);
			if (pos == val_str.npos)
			{
				l_str = val_str;
				bk = true;
			}
			else
			{
				l_str = val_str.substr(0, pos);
				val_str = val_str.substr(pos+1, val_str.length());
				val_str.erase(0, val_str.find_first_not_of(" \t"));
			}

			str2type(l_str, val);
			out_vec.push_back(val);
			if (bk) break;
		}
		return 0;
	}

	/**
	 * @brief      Parse a string argument into separated elements.
	 *
	 * @param[in]  val_str    The input value string
	 * @param[in]  separator  The separator
	 * @param[in]  throw_err  Throw out errors
	 * @param[in]  fst_val    The output values returned by quote
	 *
	 * @tparam     ValueType  Runtime value type
	 * 
	 * @return     Number of parsed strings
	 */
	template <typename ValueType>
	int parse_string_to_value(std::string val_str, char separator, bool throw_err, ValueType &fst_val)
	{
		std::string tmp_str;
		std::stringstream tmp_ss(val_str);
		std::getline(tmp_ss, tmp_str, separator);

		tmp_ss.clear();
		tmp_ss.str(tmp_str);
		tmp_ss >> fst_val;

		if (throw_err && tmp_ss.fail())
		{
			throw std::runtime_error("Fail to parse the input string. From gctl::parse_string_to_value(...)");
		}
		else if (tmp_ss.fail())
		{
			return 0;
		}
		return 1;
	}

	/**
	 * @brief      Parse a string argument into separated elements.
	 *
	 * @param[in]  val_str    The input value string
	 * @param[in]  separator  The separator
	 * @param[in]  throw_err  Throw out errors
	 * @param[in]  fst_val    The output values returned by quote
	 *
	 * @tparam     ValueType  Runtime value type
	 * @tparam     Args       Runtime value types of the rest arguments
	 * 
	 * @return     Number of parsed strings
	 */
	template <typename ValueType, typename... Args>
	int parse_string_to_value(std::string val_str, char separator, bool throw_err, ValueType &fst_val, Args&... rest)
	{
		std::string tmp_str;
		std::stringstream tmp_ss(val_str);
		std::getline(tmp_ss, tmp_str, separator);

		tmp_ss.clear();
		tmp_ss.str(tmp_str);
		tmp_ss >> fst_val;

		if (throw_err && tmp_ss.fail())
		{
			throw std::runtime_error("Fail to parse the input string. From gctl::parse_string_to_value(...)");
		}
		else if (tmp_ss.fail())
		{
			return 0;
		}

		int position = val_str.find_first_of(separator);

		if (position == std::string::npos) tmp_str = "";
		else tmp_str = val_str.substr(position+1, val_str.length());

		return parse_string_to_value(tmp_str, separator, throw_err, rest...)  + 1;
	}
}

#endif // _GCTL_STREAM_TEMPLATE_H